#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/miscdevice.h>

MODULE_LICENSE("GPL");

#define MODULENAME "platformbus" // 与platform_device的name相同

#define LOG_INFO(fmt, ...)                \
    printk(KERN_INFO MODULENAME "driver"  \
                                ": " fmt, \
           ##__VA_ARGS__)

#define LOG_ERR(fmt, ...)                \
    printk(KERN_ERR MODULENAME "driver"  \
                               ": " fmt, \
           ##__VA_ARGS__)

static struct resource *res_mem = NULL;  // IORESOURCE_MEM 类型的资源
struct resource *res_mem_request = NULL; // IO 端口内存请求，如果成功会返回一个资源指针
unsigned int* vir_gpio5_dr = NULL; // IO端口映射到内核空间的虚拟地址
extern struct miscdevice misc; // defined in "misc.c"



int platformbusdriver_probe(struct platform_device *pdev)
{
    LOG_INFO("platformbusdriver_probe");

    // res = pdev->resource;// 不推荐，不安全

    // 参数分别是 platform_device, 资源类型, 资源索引（同类资源中的第几个而不是在数组中的索引值）
    res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res_mem == NULL)
    {
        LOG_ERR("platform_get_resource failed\n");
        return -EBUSY;
    }

    LOG_INFO("platform_get_resource success\n");
    LOG_INFO("res_mem->start = 0x%08x, "
             "res_mem->end = 0x%08x, "
             "res_mem->flags = 0x%08x, "
             "res_mem->name = %s\n",
             res_mem->start, res_mem->end,
             res_mem->flags, res_mem->name);

#if 0 // 一定会请求失败，所以这里注释掉
    {// 请求 IO 端口
        res_mem_request = request_mem_region(res_mem->start,
                                         resource_size(res_mem),
                                         res_mem->name);

        if (res_mem_request == NULL)
        {
            LOG_ERR("request_mem_region failed\n");
            //  这里直接返回就行，不需要释放资源，视频里面有错误，而且有安全风险
            // 因为这里并没有成功的申请到资源，所以不需要释放，而视频里面释放了
            // 如果在释放的时候，这个资源已经被其他驱动申请了，那么就会出现很严重的问题
            return -EBUSY;
        }
        else
        {
            LOG_INFO("request_mem_region success\n");
        }
    }
#endif

    { // ioremap
        vir_gpio5_dr = ioremap(res_mem->start, resource_size(res_mem));
        if (vir_gpio5_dr == NULL)
        {
            LOG_ERR("ioremap failed\n");
            return -EBUSY;
        }
        else
        {
            LOG_INFO("ioremap success, vir_gpio5_dr = 0x%08x\n", vir_gpio5_dr);
        }
    }

    {// 注册杂项设备
        int ret = misc_register(&misc);
        if (ret < 0)
        {
            LOG_ERR("misc_register failed\n");
            return ret;
        }
        else
        {
            LOG_INFO("misc_register success\n");
        }
    }

    return 0;
}

int platformbusdriver_remove(struct platform_device *pdev)
{
    misc_deregister(&misc);

    if (vir_gpio5_dr != NULL)
    {
        iounmap(vir_gpio5_dr);
    }

    if (res_mem_request != NULL)
    {
        release_mem_region(res_mem->start, resource_size(res_mem));
    }

    LOG_INFO("platformbusdriver_remove");
    return 0;
}

struct platform_driver driver = {
    .probe = platformbusdriver_probe,
    .remove = platformbusdriver_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = MODULENAME,
    },
};

static int __init platformbusdriver_init(void)
{
    LOG_INFO("Hello, platformbusdriver\n");

    { // 注册platform_driver
        int ret = platform_driver_register(&driver);
        if (ret < 0)
        {
            LOG_ERR("platform_driver_register failed\n");
            return ret;
        }
        else
        {
            LOG_INFO("platform_driver_register success\n");
        }
    }

    return 0;
}

static void platformbusdriver_exit(void)
{

    { // 注销platform_driver
        platform_driver_unregister(&driver);
    }

    LOG_INFO("Goodbye, platformbusdriver\n");
}

module_init(platformbusdriver_init);
module_exit(platformbusdriver_exit);
